/*
 * GameDisplayManager.java
 * 
 * An abstract class for managing the display of the game.
 * It takes care of setting up the JFrame, Canvas, and BufferStrategy with double-buffering.
 * It also handles changing to/from fullscreen mode.
 * 
 */
package dk.rofl.graphics;

import dk.eleit.input.InputManager;
import java.awt.*;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;

/**
 *
 * @author menderleit
 */
public abstract class GameDisplayManager {

    private static DisplayMode dm;
    private static String title;
    private static JFrame frame;
    private static Canvas canvas;
    private static BufferStrategy bs;
    private static InputManager im;
    private static boolean fullScreen;

    // Initialize the Game Display.
    public static void init(DisplayMode displayMode, String displayTitle, boolean fullScreenMode) {
        // Store the DisplayMode and Title.
        dm = displayMode;
        title = displayTitle;
        fullScreen = fullScreenMode;
        
        // Dispose of the old display if we did not call exit() beforehand.
        if (frame != null || bs != null) {
            exit();
        }

        // Setup the JFrame.
        frame = new JFrame();

        // Make the window undecorated and alwaysOnTop if fullscreen.
        if (fullScreen) {
            frame.setUndecorated(true);
            frame.setAlwaysOnTop(true);
        } else {
            frame.setUndecorated(false);
            frame.setAlwaysOnTop(false);
        }
        frame.setResizable(false);
        frame.setTitle(title);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

        // Make sure the JFrame size takes borders into account.
        Insets inset = frame.getInsets();
        frame.setSize(dm.getWidth() + inset.left + inset.right, dm.getHeight() + inset.top + inset.bottom);

        // Position the window in the upper left corner if fullscreen mode, else center it on the screen.
        if (fullScreen) {
            frame.setLocation(0, 0);
        } else {
            frame.setLocationRelativeTo(null);
        }

        // Setup the Canvas.
        canvas = new Canvas();
        canvas.setSize(dm.getWidth(), dm.getHeight());
        canvas.setBackground(new Color(0, 0, 0));

        // Add the Canvas to the JFrame.
        frame.add(canvas);

        // Create a BufferCapabilities object using hardware-acceleration.
        ImageCapabilities ic = new ImageCapabilities(true);
        BufferCapabilities bc = new BufferCapabilities(ic, ic, BufferCapabilities.FlipContents.PRIOR);

        // Try to create a BufferStrategy using hardware-acceleration.
        try {
            canvas.createBufferStrategy(2, bc);
            bs = canvas.getBufferStrategy();
        } catch (AWTException ex) {
            // If that doesn't work, use whatever we can get.
            canvas.createBufferStrategy(2);
            bs = canvas.getBufferStrategy();
        }

        // Add our InputManager the Canvas.
        im = InputManager.getInstance();
        canvas.addKeyListener(im);
        canvas.addMouseListener(im);
        canvas.addMouseMotionListener(im);

        // Set fullscreen mode if needed.
        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        if(fullScreen) {
            if (gd.isFullScreenSupported()) {
                gd.setFullScreenWindow(frame);
                gd.setDisplayMode(dm);
            } else {
                fullScreen = false;
                gd.setFullScreenWindow(null);
            }
        } else {
            gd.setFullScreenWindow(null);
        }
        
        // Make sure the JFrame is in the front, and our Canvas has focus.
        frame.toFront();
        canvas.requestFocus();
    }

    // Sets the display to fullscreen mode or windowed mode based on the parameter.
    public static void setFullScreenMode(boolean showFullScreen) {
        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        if (showFullScreen && !fullScreen) {
            if (gd.isFullScreenSupported()) {
                init(dm, title, true);
            }
        }
        if (!showFullScreen && fullScreen) {
            init(dm, title, false);
        }
    }

    // Switches the display to/from fullscreen mode.
    public static void switchFullscreenMode() {
        if (fullScreen) {
            setFullScreenMode(false);
        } else {
            setFullScreenMode(true);
        }
    }

    // Returns true if the display is in fullscreen mode.
    public static boolean isFullscreenMode() {
        return fullScreen;
    }

    // Close down the Display.
    public static void exit() {
        // Make sure we are not in fullscreen mode.
        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        gd.setFullScreenWindow(null);

        // Get rid off the BufferStrategy and the JFrame.
        if (bs != null) {
            bs.dispose();
        }
        if (frame != null) {
            frame.dispose();
        }
    }

    // Return a reference to the JFrame.
    public static JFrame getJFrame() {
        return frame;
    }

    // Return a reference to the Canvas.
    public static Canvas getCanvas() {
        return canvas;
    }

    // Return a reference to the BufferStrategy.
    public static BufferStrategy getBufferStrategy() {
        return bs;
    }

    // Return a Graphics2D object to our back-buffer.
    public static Graphics2D getGraphics() {
        return (Graphics2D) bs.getDrawGraphics();
    }

    // Blits/Flips the back-buffer to the front-buffer.
    public static void show() {
        bs.show();
    }

    // Return a reference to the InputManager.
    public static InputManager getInputManager() {
        return im;
    }
    
    // Return a reference to the DisplayMode.
    public static DisplayMode getDisplayMode() {
        return dm;
    }
}
